
package com.trydofor.id.xnet;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.transport.socket.SocketConnector;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.trydofor.id.body.Bodies;
import com.trydofor.id.body.shell.ShellQueryBody;
import com.trydofor.id.body.shell.ShellReplyBody;
import com.trydofor.id.body.shell.UsageQueryBody;
import com.trydofor.id.body.shell.UsageReplyBody;
import com.trydofor.id.byta.Bytable;
import com.trydofor.id.byta.Head;
import com.trydofor.id.byta.Packet;
import com.trydofor.id.doer.shell.ArgHelp;
import com.trydofor.id.doer.shell.Command;
import com.trydofor.id.doer.shell.Outcome;
import com.trydofor.id.mina.LoggingFilter;
import com.trydofor.id.mina.PacketDecoder;
import com.trydofor.id.mina.PacketEncoder;

/**
 * @author : Shi Rongjiu (www.trydofor.com)
 */
public class GuiShell extends JFrame {
    
    private static final Logger       LOGGER           = LoggerFactory.getLogger(GuiShell.class);
    
    private static final String       TITLE            = "Gui-Shell-0.9";
    private static final String       SPLITER          = "/";
    private static final long         serialVersionUID = 1L;
    
    private static final Border       BORDER_GRACE     = BorderFactory.createLineBorder(Color.GRAY);
    private static final Border       BORDER_LGRED     = BorderFactory.createLineBorder(Color.RED);
    private static final Border       BORDER_EMPTY     = BorderFactory.createEmptyBorder(5, 5, 5, 5);
    private static final Border       BORDER_COMPD     = BorderFactory.createCompoundBorder(BORDER_GRACE,
                                                               BORDER_EMPTY);
    private static final Font         FONT_PLAIN       = new Font("Monospaced", Font.PLAIN, 12);
    private static final Font         FONT_BLOD        = new Font("Monospaced", Font.BOLD, 12);
    private static final Color        COLOR_REQUIRED   = new Color(204, 255, 153);
    
    private Map<String, String>       mapNdCmd         = new HashMap<String, String>();
    private Map<String, Command>      mapIdCmd         = new HashMap<String, Command>();
    private Map<String, Outcome>      mapOcCmd         = new HashMap<String, Outcome>();
    private List<JLabel>              lstLabel         = new ArrayList<JLabel>();
    private List<JComboBox>           lstCombo         = new ArrayList<JComboBox>();
    private List<JTextField>          lstField         = new ArrayList<JTextField>();
    
    private transient SocketConnector connector        = null;
    private transient IoSession       iosession        = null;
    
    private Command                   cmdConnect       = null;
    private Command                   cmdDisconnect    = null;
    
    private JSplitPane                splMainPane      = null;
    private JTree                     treCmdTree       = null;
    private JTabbedPane               tabCmdPane       = null;
    private JPanel                    pnlCmdIn         = null;
    private JSplitPane                splCmdOut        = null;
    private DefaultMutableTreeNode    tndCmdRoot       = null;
    private JTextField                txtCmdName       = null;
    private JButton                   btnRefreshCmd    = null;
    private JScrollPane               scpCmdUsage      = null;
    private JTextArea                 txtCmdUsage      = null;
    private JTextArea                 txtCmdOut        = null;
    private JTextArea                 txtCmdErr        = null;
    private JPanel                    pnlSysLog        = null;
    private JScrollPane               scpSysLog        = null;
    private JScrollPane               spnCmdArgs       = null;
    private JTextArea                 txtSysLog        = null;
    private JButton                   btnLogClean      = null;
    private JButton                   btnExecute       = null;
    private JButton                   btnResetArg      = null;
    
    private JButton                   btnCmdToXml      = null;
    
    /**
     * This is the default constructor
     */
    public GuiShell() {
        super();
        initialize();
    }
    
    private void initialize() {
        this.setContentPane(getSplMainPane());
        this.setTitle(TITLE);
        this.setResizable(false);
        // center
        Dimension ss = Toolkit.getDefaultToolkit().getScreenSize();
        int sw = (int) ss.getWidth();
        int sh = (int) ss.getHeight();
        int fw = 815;
        int fh = 535;
        this.setBounds(300, 50, 815, 535);
        this.setFont(FONT_PLAIN);
        this.addWindowListener(new java.awt.event.WindowAdapter() {
            
            public void windowClosing(java.awt.event.WindowEvent e) {
                onDisconnect();
            }
        });
        this.setBounds((sw - fw) / 2, (sh - fh) / 2, fw, fh);
    }
    
    // --------------- initXXXX ---------------
    
    private void initConnector() {
        PacketDecoder decoder = new PacketDecoder();
        decoder.register(Bodies.SHELLQUERY);
        decoder.register(Bodies.SHELLREPLY);
        decoder.register(Bodies.USAGEQUERY);
        decoder.register(Bodies.USAGEREPLY);
        ProtocolCodecFilter codecFilter = new ProtocolCodecFilter(new PacketEncoder(), decoder);
        LoggingFilter logingFilter = new LoggingFilter(LOGGER);
        
        connector = new NioSocketConnector();
        connector.getFilterChain().addLast("codec", codecFilter);
        connector.getFilterChain().addLast("logger", logingFilter);
        
        connector.setHandler(new IoHandlerAdapter() { // not UI-thread
        
            @Override
            public void messageReceived(IoSession session, Object message) throws Exception {
                final Packet reply = (Packet) message; // throw
                final Bytable<?> body = reply.getBody();
                if (body instanceof UsageReplyBody) {
                    onServerReply((UsageReplyBody) body);
                }
                else if (body instanceof ShellReplyBody) {
                    onServerReply((ShellReplyBody) body);
                }
                else {
                    LOGGER.error("unsupported packet," + body);
                }
            }
            
            public void sessionClosed(IoSession session) throws Exception {
                SwingUtilities.invokeLater(new Runnable() {
                    
                    public void run() {
                        onDisconnect();
                        resetCmdInOut();
                    }
                });
            }
            
            @Override
            public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
                // let logging filter do
            }
        });
    }
    
    private void initBuiltinCmd() {
        cmdConnect = new Command("connect", "connect to server");
        ArgHelp arg1 = new ArgHelp("host");
        arg1.setManual("the host ip or name");
        arg1.setRequired(true);
        arg1.setEditable(true);
        arg1.addOption("127.0.0.1");
        for (InetAddress ip : ConfBuilder.getInetAddress()) {
            arg1.addOption(ip.getHostAddress());
        }
        
        ArgHelp arg2 = new ArgHelp("port");
        arg2.setManual("the port of host");
        arg2.setPattern("^\\d+$");
        arg2.setDefault(String.valueOf(Constant.SERVICE_PORT));
        arg2.setRequired(true);
        
        ArgHelp arg3 = new ArgHelp("timeout");
        arg3.setManual("the timeout(second) to wait");
        arg3.setPattern("^\\d+$");
        arg3.setDefault(String.valueOf(10));
        
        cmdConnect.addArgument(arg1, arg2, arg3);
        cmdDisconnect = new Command("disconnect", "disconnect the server");
        
        resetCmdTree();
    }
    
    private void initOutErrput() {
        getTxtSysLog().setText("---------------\n");
        
        Thread t = new Thread() {
            
            public void run() {
                try {
                    PipedOutputStream pos = new PipedOutputStream();
                    PrintStream ps = new PrintStream(pos);
                    System.setErr(ps);
                    System.setOut(ps);
                    PipedInputStream pis = new PipedInputStream(pos);
                    BufferedReader br = new BufferedReader(new InputStreamReader(pis));
                    while (true) {
                        String ln = br.readLine();
                        getTxtSysLog().append(ln + "\n");
                    }
                }
                catch (IOException e) {
                    // ignore
                }
            }
        };
        
        t.start();
    }
    
    // --------------- resetXXXX ---------------
    
    private void resetCmdTree() {
        // clean tree
        getTndCmdRoot().removeAllChildren();
        mapNdCmd.clear();
        mapIdCmd.clear();
        mapOcCmd.clear();
        
        onRefreshCmd("[client]" + SPLITER + cmdConnect.getAlias(), cmdConnect);
        onRefreshCmd("[client]" + SPLITER + cmdDisconnect.getAlias(), cmdDisconnect);
    }
    
    private void resetCmdInOut() {
        // hide
        for (JLabel c : lstLabel) {
            c.setVisible(false);
        }
        for (JComboBox c : lstCombo) {
            c.setVisible(false);
        }
        for (JTextField c : lstField) {
            c.setVisible(false);
        }
        
        getTxtCmdName().setText("");
        getTxtCmdUsage().setText("");
        getTxtCmdOut().setText("");
        getTxtCmdErr().setText("");
        
        getTxtCmdOut().setBorder(BORDER_GRACE);
        getTxtCmdOut().setBorder(BORDER_GRACE);
        splCmdOut.setDividerLocation(0.6);
    }
    
    // --------------- onXXXX ---------------
    
    // no-ui-thread
    private void onServerReply(final UsageReplyBody body) {
        SwingUtilities.invokeLater(new Runnable() {
            
            public void run() {
                Map<String, Command> cmds = body.getAll();
                ArrayList<Entry<String, Command>> al = new ArrayList<Entry<String, Command>>();
                al.addAll(cmds.entrySet());
                Collections.sort(al, new Comparator<Entry<String, Command>>() {
                    
                    @Override
                    public int compare(Entry<String, Command> o1, Entry<String, Command> o2) {
                        return o1.getKey().compareTo(o2.getKey());
                    }
                });
                
                for (Entry<String, Command> e : al) {
                    onRefreshCmd(e.getKey(), e.getValue());
                }
            }
        });
    }
    
    // no-ui-thread
    private void onServerReply(final ShellReplyBody body) {
        SwingUtilities.invokeLater(new Runnable() {
            
            public void run() {
                if (body.getCommand() != null) {
                    mapOcCmd.put(body.getCommand(), body.getOutcome());
                }
                onShowCmdOut(body.getOutcome());
            }
        });
        
    }
    
    private void onShowCmdOut(Outcome out) {
        if (out == null) return;
        
        String stat = "============= [return status: " + out.status() + "] =============\n";
        
        if (out.outPut() == null || out.outPut().length() == 0) { // no out
            splCmdOut.setDividerLocation(0.0);
            getTxtCmdErr().setText(stat + out.errPut());
            getTxtCmdOut().setBorder(BORDER_LGRED);
        }
        else if (out.errPut() == null || out.errPut().length() == 0) { // no err
            splCmdOut.setDividerLocation(1.0);
            getTxtCmdOut().setText(stat + out.outPut());
            getTxtCmdOut().setBorder(BORDER_GRACE);
        }
        else { // both
            getTxtCmdOut().setText(stat + out.outPut());
            getTxtCmdErr().setText(out.errPut());
            getTxtCmdOut().setBorder(BORDER_LGRED);
            getTxtCmdOut().setBorder(BORDER_GRACE);
            splCmdOut.setDividerLocation(0.6);
        }
        
        tabCmdPane.setSelectedComponent(getSplCmdOut());
    }
    
    private void onMainResize(int off) {
        Dimension du = scpCmdUsage.getSize();
        scpCmdUsage.setSize(du.width + off, du.height);
        
        Dimension da = getSpnCmdArgs().getSize();
        getSpnCmdArgs().setSize(da.width + off, da.height);
        
        Dimension ds = scpSysLog.getSize();
        scpSysLog.setSize(ds.width + off, ds.height);
    }
    
    private void onRefreshCmd(String id, Command command) {
        DefaultMutableTreeNode np = null;
        
        if (mapIdCmd.containsKey(id)) {
            mapIdCmd.put(id, command);
            return; // update command only
        }
        
        // update tree
        if (id.indexOf(SPLITER) <= 0) {
            np = new DefaultMutableTreeNode(id, false);
            getTndCmdRoot().add(np);
        }
        else {
            String[] nodes = id.split(SPLITER);
            int path = nodes.length - 1;
            
            DefaultMutableTreeNode node = getTndCmdRoot();
            for (int i = 0; i < path; i++) {
                @SuppressWarnings("rawtypes")
                Enumeration e = node.children();
                DefaultMutableTreeNode cur = null;
                while (e.hasMoreElements()) {
                    DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
                    if (nodes[i].equals(n.getUserObject())) {
                        cur = n;
                        break;
                    }
                }
                if (cur == null) {
                    cur = new DefaultMutableTreeNode(nodes[i]);
                    node.add(cur);
                }
                node = cur;
            }
            
            np = new DefaultMutableTreeNode(nodes[nodes.length - 1], false);
            node.add(np);
        }
        getTreCmdTree().updateUI();
        mapNdCmd.put(Arrays.toString(np.getPath()), id);
        mapIdCmd.put(id, command);
    }
    
    private void onCmdTree(TreePath path) {
        if (path == null) return;
        String np = Arrays.toString(path.getPath());
        String id = mapNdCmd.get(np);
        if (id == null) return;
        onSelectCmd(id);
    }
    
    private void onClientQuery(Bytable<?> body) {
        Head head = new Head(0, System.currentTimeMillis());
        Packet packet = new Packet(head, body);
        iosession.write(packet);
    }
    
    private void onConnect(Map<String, String> args) {
        try {
            if (iosession != null && iosession.isConnected()) {
                iosession.close(true);
            }
            
            String host = args.get("host");
            int port = Integer.parseInt(args.get("port"));
            ConnectFuture connectFuture = connector.connect(new InetSocketAddress(host, port));
            int timeout = Integer.parseInt(args.get("timeout"));
            connectFuture.awaitUninterruptibly(timeout * 1000);
            LOGGER.info("connection to " + host + ":" + port);
            iosession = connectFuture.getSession();
            if (iosession == null) {
                getTxtCmdErr().setText("Can NOT connection to the server");
            }
            else {
                iosession.getConfig().setBothIdleTime(timeout);
                String mes = iosession.getLocalAddress() + "->" + host + ":" + port;
                getTxtCmdOut().setText("connection to the server, " + mes);
                this.setTitle(TITLE + " @" + mes);
                
                // ask all command
                onClientQuery(new UsageQueryBody(null));
            }
            getTabCmdPane().setSelectedComponent(getSplCmdOut());
        }
        catch (Exception e) {
            LOGGER.error("connection error", e);
            getTabCmdPane().setSelectedComponent(getPnlSysLog());
        }
    }
    
    private void onDisconnect() {
        if (iosession != null && iosession.isConnected()) {
            iosession.close(true);
        }
        this.setTitle(TITLE);
        resetCmdTree();
    }
    
    private void onRefreshCmd(String id) {
        UsageQueryBody body = new UsageQueryBody(id);
        onClientQuery(body);
    }
    
    private void onCmdToXml(String id) {
        Command cmd = mapIdCmd.get(id);
        if (cmd == null) return;
        
        StringBuilder sb = new StringBuilder();
        Collection<ArgHelp> args = cmd.getArgumentAll();
        sb.append("<cmd name=\"").append(id);
        if (args.size() > 0) {
            sb.append("\">\n");
            int index = 0;
            for (ArgHelp arg : args) {
                sb.append("    <arg name=\"").append(arg.getName()).append("\">");
                String d = null;
                if (arg.getOptions().size() > 0) {
                    JComboBox jc = lstCombo.get(index);
                    d = (String) jc.getSelectedItem();
                }
                else {
                    JTextField jc = lstField.get(index);
                    d = jc.getText();
                }
                d = d.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
                sb.append(d).append("</arg>\n");
                index++;
            }
            sb.append("</cmd>\n");
        }
        else {
            sb.append("\" />\n");
        }
        
        onShowCmdOut(new Outcome(0, sb.toString(), null));
    }
    
    private void onExecuteCmd(String id) {
        Command cmd = mapIdCmd.get(id);
        if (cmd == null) return;
        
        // valid
        int index = 0;
        boolean valid = true;
        ShellQueryBody body = new ShellQueryBody(id);
        
        for (ArgHelp arg : cmd.getArgumentAll()) {
            String d = null;
            JComponent c = null;
            if (arg.getOptions().size() > 0) {
                JComboBox jc = lstCombo.get(index);
                
                d = (String) jc.getSelectedItem();
                c = jc;
            }
            else {
                JTextField jc = lstField.get(index);
                d = jc.getText();
                c = jc;
            }
            
            if (arg.validate(d) != null) {
                c.setBorder(BORDER_LGRED);
                valid = false;
            }
            else {
                c.setBorder(BORDER_GRACE);
                body.addArgument(arg.getName(), d);
            }
            index++;
        }
        
        if (!valid) return;
        
        if (cmd == cmdConnect) {
            onConnect(body.getArgument());
        }
        else if (cmd == cmdDisconnect) {
            onDisconnect();
        }
        else {
            onClientQuery(body);
        }
    }
    
    private JLabel getArgName(int index) {
        JLabel lbl = null;
        if (lstLabel.size() <= index) {
            lbl = new JLabel();
            lbl.setBounds(5, 30 + 25 * index, 100, 20);
            lbl.setFont(FONT_BLOD);
            lbl.setHorizontalAlignment(JLabel.RIGHT);
            getSpnCmdArgs().add(lbl, null);
            lstLabel.add(lbl);
        }
        else {
            lbl = lstLabel.get(index);
        }
        return lbl;
    }
    
    private JComponent getArgData(int index, ArgHelp arg) {
        if (lstCombo.size() <= index) {
            JComboBox cmb = new JComboBox();
            cmb.setBounds(120, 30 + 25 * index, 500, 20);
            cmb.setFont(FONT_PLAIN);
            getSpnCmdArgs().add(cmb, null);
            lstCombo.add(cmb);
        }
        if (lstField.size() <= index) {
            JTextField txt = new JTextField();
            txt.setBounds(120, 30 + 25 * index, 500, 20);
            txt.setFont(FONT_PLAIN);
            getSpnCmdArgs().add(txt, null);
            lstField.add(txt);
        }
        
        JTextField txt = lstField.get(index);
        txt.setVisible(false);
        JComboBox cmb = lstCombo.get(index);
        cmb.setVisible(false);
        
        if (arg.getOptions().size() > 0) {
            cmb.removeAllItems();
            for (String opt : arg.getOptions()) {
                cmb.addItem(opt);
            }
            if (arg.getDefault() != null && arg.getOptions().contains(arg.getDefault())) {
                cmb.setSelectedItem(arg.getDefault());
            }
            else {
                cmb.setSelectedIndex(0);
            }
            cmb.setEditable(arg.isEditable());
            cmb.setVisible(true);
            return cmb;
        }
        else {
            txt.setText(arg.getDefault());
            txt.setEditable(arg.isEditable());
            txt.setVisible(true);
            return txt;
        }
    }
    
    private void onSelectCmd(String id) {
        Command cmd = mapIdCmd.get(id);
        if (cmd == null) return;
        
        resetCmdInOut();
        
        // input
        txtCmdName.setText(id);
        txtCmdUsage.setText("Name:" + cmd.getAlias() + "\n" + cmd.getUsage());
        
        Collection<ArgHelp> cas = cmd.getArgumentAll();
        getBtnRefreshCmd().setEnabled(cmdConnect != cmd && cmdDisconnect != cmd);
        getBtnResetArg().setEnabled(cas.size() > 0);
        getBtnExecute().setEnabled(!(cmdDisconnect == cmd && iosession == null));
        
        int index = 0;
        for (ArgHelp arg : cmd.getArgumentAll()) {
            // name
            JLabel lbl = getArgName(index);
            
            lbl.setText(arg.getName() + "=");
            lbl.setVisible(true);
            
            // data
            
            StringBuilder tip = new StringBuilder();
            if (arg.getPattern() != null) {
                tip.append("/").append(arg.getPattern()).append("/");
            }
            if (arg.getManual() != null) {
                tip.append(" - ").append(arg.getManual());
            }
            JComponent c = getArgData(index, arg);
            c.setBackground(arg.isRequired() ? COLOR_REQUIRED : getBackground());
            c.setToolTipText(tip.toString());
            c.setBorder(BORDER_GRACE);
            c.setVisible(true);
            //
            index++;
        }
        
        // output
        Outcome oc = mapOcCmd.get(id);
        if (oc != null) {
            onShowCmdOut(oc);
            tabCmdPane.setSelectedComponent(getSplCmdOut());
        }
        else {
            tabCmdPane.setSelectedComponent(getPnlCmdIn());
        }
    }
    
    // ---------------- auto code -------------
    
    private JSplitPane getSplMainPane() {
        if (splMainPane == null) {
            splMainPane = new JSplitPane();
            splMainPane.setBounds(new Rectangle(5, 5, 800, 500));
            splMainPane.setDividerSize(5);
            splMainPane.setDividerLocation(150);
            splMainPane.setLeftComponent(new JScrollPane(getTreCmdTree()));
            splMainPane.setRightComponent(getTabCmdPane());
            splMainPane.setFont(FONT_PLAIN);
            splMainPane.addPropertyChangeListener("dividerLocation", new java.beans.PropertyChangeListener() {
                
                public void propertyChange(java.beans.PropertyChangeEvent e) {
                    int off = ((Integer) e.getOldValue()).intValue() - ((Integer) e.getNewValue()).intValue();
                    onMainResize(off);
                }
            });
        }
        return splMainPane;
    }
    
    private DefaultMutableTreeNode getTndCmdRoot() {
        if (tndCmdRoot == null) {
            tndCmdRoot = new DefaultMutableTreeNode("Command");
        }
        return tndCmdRoot;
    }
    
    private JTree getTreCmdTree() {
        if (treCmdTree == null) {
            treCmdTree = new JTree(getTndCmdRoot());
            treCmdTree.setFont(FONT_PLAIN);
            treCmdTree.setBorder(BORDER_EMPTY);
            treCmdTree.addMouseListener(new java.awt.event.MouseAdapter() {
                
                public void mouseClicked(java.awt.event.MouseEvent e) {
                    JTree src = (JTree) e.getSource();
                    TreePath path = src.getSelectionPath();
                    onCmdTree(path);
                }
            });
        }
        
        return treCmdTree;
    }
    
    private JTabbedPane getTabCmdPane() {
        if (tabCmdPane == null) {
            tabCmdPane = new JTabbedPane();
            tabCmdPane.addTab("Usage/Input", null, getPnlCmdIn(), "command usage and inputs");
            tabCmdPane.addTab("Output/Errput", null, getSplCmdOut(), "command output and errput");
            tabCmdPane.addTab("System Log", null, getPnlSysLog(), "system log");
            tabCmdPane.setFont(FONT_PLAIN);
        }
        return tabCmdPane;
    }
    
    private JPanel getPnlCmdIn() {
        if (pnlCmdIn == null) {
            pnlCmdIn = new JPanel();
            pnlCmdIn.setLayout(null);
            pnlCmdIn.add(getBtnRefreshCmd(), null);
            scpCmdUsage = new JScrollPane(getTxtCmdUsage());
            scpCmdUsage.setBounds(new Rectangle(100, 5, 540, 70));
            scpCmdUsage.setBorder(BORDER_COMPD);
            pnlCmdIn.add(scpCmdUsage, null);
            pnlCmdIn.setFont(FONT_PLAIN);
            pnlCmdIn.add(getSpnCmdArgs(), null);
            pnlCmdIn.add(getBtnExecute(), null);
            pnlCmdIn.add(getBtnResetArg(), null);
        }
        return pnlCmdIn;
    }
    
    private JSplitPane getSplCmdOut() {
        if (splCmdOut == null) {
            splCmdOut = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
            splCmdOut.setBounds(new Rectangle(5, 5, 635, 455));
            splCmdOut.setDividerSize(5);
            splCmdOut.setDividerLocation(150);
            splCmdOut.setFont(FONT_PLAIN);
            
            JScrollPane tco = new JScrollPane(getTxtCmdOut());
            tco.setBorder(BORDER_EMPTY);
            splCmdOut.setTopComponent(tco);
            
            JScrollPane tce = new JScrollPane(getTxtCmdErr());
            tce.setBorder(BORDER_EMPTY);
            splCmdOut.setBottomComponent(tce);
            
        }
        return splCmdOut;
    }
    
    private JPanel getPnlSysLog() {
        if (pnlSysLog == null) {
            pnlSysLog = new JPanel();
            
            pnlSysLog.setLayout(null);
            pnlSysLog.setFont(FONT_PLAIN);
            scpSysLog = new JScrollPane(getTxtSysLog());
            scpSysLog.setBorder(BORDER_COMPD);
            scpSysLog.setBounds(new Rectangle(5, 5, 635, 430));
            pnlSysLog.add(scpSysLog, null);
            pnlSysLog.add(getBtnLogClean(), null);
        }
        return pnlSysLog;
    }
    
    private JLabel getLblCmdName() {
        JLabel lbl = new JLabel();
        lbl.setBounds(5, 5, 100, 20);
        lbl.setFont(FONT_BLOD);
        lbl.setHorizontalAlignment(JLabel.RIGHT);
        lbl.setText("[command]=");
        return lbl;
    }
    
    private JTextField getTxtCmdName() {
        if (txtCmdName == null) {
            txtCmdName = new JTextField();
            txtCmdName.setBounds(120, 5, 450, 20);
            txtCmdName.setBorder(BORDER_GRACE);
            txtCmdName.setVisible(true);
            txtCmdName.setEditable(false);
        }
        return txtCmdName;
    }
    
    private JButton getBtnCmdToXml() {
        if (btnCmdToXml == null) {
            btnCmdToXml = new JButton();
            btnCmdToXml.setBounds(580, 5, 40, 20);
            btnCmdToXml.setBorder(BORDER_GRACE);
            btnCmdToXml.setFont(FONT_PLAIN);
            btnCmdToXml.setText("XML");
            btnCmdToXml.setVisible(true);
            btnCmdToXml.addMouseListener(new java.awt.event.MouseAdapter() {
                
                public void mouseClicked(java.awt.event.MouseEvent e) {
                    onCmdToXml(txtCmdName.getText());
                }
            });
        }
        return btnCmdToXml;
    }
    
    private JButton getBtnRefreshCmd() {
        if (btnRefreshCmd == null) {
            btnRefreshCmd = new JButton();
            btnRefreshCmd.setBounds(new Rectangle(5, 5, 90, 20));
            btnRefreshCmd.setText("Refresh");
            btnRefreshCmd.setFont(FONT_PLAIN);
            btnRefreshCmd.setEnabled(false);
            btnRefreshCmd.addMouseListener(new java.awt.event.MouseAdapter() {
                
                public void mouseClicked(java.awt.event.MouseEvent e) {
                    onRefreshCmd(txtCmdName.getText());
                }
            });
        }
        return btnRefreshCmd;
    }
    
    private JButton getBtnResetArg() {
        if (btnResetArg == null) {
            btnResetArg = new JButton();
            btnResetArg.setBounds(new Rectangle(5, 30, 90, 20));
            btnResetArg.setText("ResetArg");
            btnResetArg.setFont(FONT_PLAIN);
            btnResetArg.setEnabled(false);
            btnResetArg.addMouseListener(new java.awt.event.MouseAdapter() {
                
                public void mouseClicked(java.awt.event.MouseEvent e) {
                    onSelectCmd(txtCmdName.getText());
                }
            });
        }
        return btnResetArg;
    }
    
    private JButton getBtnExecute() {
        if (btnExecute == null) {
            btnExecute = new JButton();
            btnExecute.setBounds(new Rectangle(5, 55, 90, 20));
            btnExecute.setText("Execute");
            btnExecute.setFont(FONT_PLAIN);
            btnExecute.setEnabled(false);
            btnExecute.addMouseListener(new java.awt.event.MouseAdapter() {
                
                public void mouseClicked(java.awt.event.MouseEvent e) {
                    onExecuteCmd(txtCmdName.getText());
                }
            });
        }
        return btnExecute;
    }
    
    private JTextArea getTxtCmdUsage() {
        if (txtCmdUsage == null) {
            txtCmdUsage = new JTextArea();
            txtCmdUsage.setText("command \nname \nusage");
            txtCmdUsage.setBackground(getBackground());
            txtCmdUsage.setEditable(false);
            txtCmdUsage.setFont(FONT_PLAIN);
        }
        return txtCmdUsage;
    }
    
    private JScrollPane getSpnCmdArgs() {
        if (spnCmdArgs == null) {
            spnCmdArgs = new JScrollPane();
            spnCmdArgs.setLayout(null);
            spnCmdArgs.setBounds(new Rectangle(5, 80, 635, 395));
            spnCmdArgs.setBorder(BORDER_COMPD);
            spnCmdArgs.add(getLblCmdName(), null);
            spnCmdArgs.add(getTxtCmdName(), null);
            spnCmdArgs.add(getBtnCmdToXml(), null);
        }
        return spnCmdArgs;
    }
    
    private JTextArea getTxtCmdOut() {
        if (txtCmdOut == null) {
            txtCmdOut = new JTextArea();
            txtCmdOut.setText("out put");
            txtCmdOut.setBackground(getBackground());
            txtCmdOut.setEditable(false);
            txtCmdOut.setFont(FONT_PLAIN);
            txtCmdOut.setBorder(BORDER_GRACE);
        }
        return txtCmdOut;
    }
    
    private JTextArea getTxtCmdErr() {
        if (txtCmdErr == null) {
            txtCmdErr = new JTextArea();
            txtCmdErr.setText("err put");
            txtCmdErr.setBackground(getBackground());
            txtCmdErr.setEditable(false);
            txtCmdErr.setFont(FONT_PLAIN);
            txtCmdErr.setBorder(BORDER_GRACE);
        }
        return txtCmdErr;
    }
    
    private JTextArea getTxtSysLog() {
        if (txtSysLog == null) {
            txtSysLog = new JTextArea();
            txtSysLog.setText("sys log");
            txtSysLog.setBackground(getBackground());
            txtSysLog.setEditable(true);
            txtSysLog.setFont(FONT_PLAIN);
        }
        return txtSysLog;
    }
    
    private JButton getBtnLogClean() {
        if (btnLogClean == null) {
            btnLogClean = new JButton();
            btnLogClean.setFont(FONT_PLAIN);
            btnLogClean.setText("Clean");
            btnLogClean.setBounds(new Rectangle(5, 445, 80, 24));
            btnLogClean.addMouseListener(new java.awt.event.MouseAdapter() {
                
                public void mouseClicked(java.awt.event.MouseEvent e) {
                    txtSysLog.setText("");
                }
            });
        }
        return btnLogClean;
    }
    
    // ========================================
    /**
     * @param args the arguments
     */
    public static void main(String[] args) {
        
        Runnable ui = new Runnable() {
            
            public void run() {
                GuiShell gui = new GuiShell();
                gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                gui.setVisible(true);
                gui.initOutErrput();
                gui.initBuiltinCmd();
                gui.initConnector();
            }
        };
        
        SwingUtilities.invokeLater(ui);
    }
}
